#include "zbacktrace.h"
#if ZUTIL_OSTYPE==ZUTIL_WIN32
#include <windows.h> 
#include <excpt.h> 
#include <imagehlp.h> 
//#include <psapi.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <stddef.h> 
#include <stdarg.h> 
#include <string.h> 

static const char * s_corefile = NULL;

static void rendertimer(FILE* fio)
{
	char buf[128];
	struct tm* tm;
	time_t sec=time(NULL);
	tm=localtime(&sec);
	strftime(buf, 100,"%Y/%m/%d %H:%M:%S",tm);
	fprintf(fio,buf);
}

static void addressToString(FILE* fio,void *address) 
{
	// First the raw address 
	fprintf(fio,"0x%p",address);

	// Then any name for the symbol 
	struct tagSymInfo 
	{ 
		IMAGEHLP_SYMBOL symInfo; 
		char nameBuffer[ 4 * 256 ]; 
	} SymInfo = { { sizeof( IMAGEHLP_SYMBOL ) } }; 

	IMAGEHLP_SYMBOL * pSym = &SymInfo.symInfo; 
	pSym->MaxNameLength = sizeof( SymInfo ) - offsetof( tagSymInfo, symInfo.Name ); 

	DWORD dwDisplacement; 
	if ( SymGetSymFromAddr(GetCurrentProcess(),(DWORD)address, &dwDisplacement,  pSym)) 
	{ 
		fprintf(fio," %s",pSym->Name);
		if ( dwDisplacement != 0 ) 
			fprintf(fio,"+0x%x",dwDisplacement); 
	} 

	// Finally any file/line number 
	IMAGEHLP_LINE lineInfo = { sizeof( IMAGEHLP_LINE ) }; 
	if ( SymGetLineFromAddr(GetCurrentProcess(),(DWORD)address,&dwDisplacement,&lineInfo)) 
	{ 
		char const *pDelim = strrchr( lineInfo.FileName, '\\' ); 
		fprintf(fio," at %s(%d)",(pDelim?pDelim+1:lineInfo.FileName),lineInfo.LineNumber); 
	} 
	return; 
} 


static void StackTrace(FILE* fio, PCONTEXT pContext) 
{
	fprintf(fio, "====backtrace record at ");
	rendertimer(fio);

	fprintf(fio,"  Frame       Code address\n");

	STACKFRAME stackFrame = {0}; 

	stackFrame.AddrPC.Offset = pContext->Eip; 
	stackFrame.AddrPC.Mode = AddrModeFlat; 

	stackFrame.AddrFrame.Offset = pContext->Ebp; 
	stackFrame.AddrFrame.Mode = AddrModeFlat; 

	stackFrame.AddrStack.Offset = pContext->Esp; 
	stackFrame.AddrStack.Mode = AddrModeFlat; 

	while ( ::StackWalk( 
		IMAGE_FILE_MACHINE_I386, 
		GetCurrentProcess(), 
		GetCurrentThread(), // this value doesn't matter much if previous one is a real handle 
		&stackFrame,  
		pContext, 
		NULL, 
		::SymFunctionTableAccess, 
		::SymGetModuleBase, 
		NULL)) 
	{ 
		fprintf(fio,"  0x%p  ",(void*)stackFrame.AddrFrame.Offset); 
		addressToString(fio,(void*)stackFrame.AddrPC.Offset);
		fprintf(fio,"\n");
	} 
	fflush(fio);
} 

void zGetBacktrace (char* buf, int maxsize)
{
	_snprintf(buf,maxsize,"not support on win32\n");
}

static LPTOP_LEVEL_EXCEPTION_FILTER g_prev = NULL;

static LONG WINAPI 
exception_filter(LPEXCEPTION_POINTERS info)
{
	FILE* fio=NULL;
	if (s_corefile)
	{
		fio=fopen(s_corefile,"a+");
	}
	if (fio==NULL)
		fio=stderr;
	StackTrace(fio,info->ContextRecord);
	exit(1);
	return 0;
}

int zEnableBacktrace(const char* corefile)
{
	if (corefile)
		s_corefile=corefile;
	DWORD dwOpts = SymGetOptions(); 
	dwOpts |= SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS; 
	SymSetOptions ( dwOpts ); 

	if (!SymInitialize(GetCurrentProcess(), 0, TRUE))
	{
		fprintf(stderr,"Failed to init symbol context\n");
		return -1;
	}

	SetUnhandledExceptionFilter(exception_filter);
	return 0;
}

void zSetBacktraceFile(const char* corefile)
{
	if (corefile)
		s_corefile=corefile;
}

#endif